package game;

import game.GameState.Phase;
import game.HandValue.Type;

import java.util.ArrayList;
import java.util.Scanner;
import java.util.Stack;
import java.util.regex.*;

public class GameState {
	public enum Phase{
		BLINDS,FLOP,TURN,RIVER,WINNER;
	}
	private ArrayList<Card> tableCards;
	private ArrayList<Player> players;
	private Phase currentPhase;
	private int centerPot;
	private int currentPlayerTurn;
	private int currentHandQualifications;

	public GameState(){
		centerPot = 0;
		currentPlayerTurn = 0;
		currentHandQualifications = 0;
		tableCards = new ArrayList<Card>();
		players = new ArrayList<Player>();

	}
	public void addPlayer(String playerName){
		Player player = new Player();
		player.setName(playerName);
		players.add(player);
	}
	public GameState(String updatedGameState){
		Scanner scan = new Scanner(updatedGameState);
		String line1 = scan.nextLine();
		String tableCards = line1.substring(12, line1.length() - 1);
		Pattern p = Pattern.compile("(\\{)(.*?)(\\})");
		Matcher match = p.matcher(tableCards);
		this.tableCards = new ArrayList<Card>();
		while (match.find()){
			String cardRep = match.group(2);
			this.tableCards.add(new Card(cardRep));
		}
		String line2 = scan.nextLine();
		String players = line2.substring(9,line2.length() - 1);
		match = p.matcher(players);
		this.players = new ArrayList<Player>();
		while (match.find()){
			String playerRep = match.group(2);
			this.players.add(new Player(playerRep));
		}
		String line3 = scan.nextLine();
		String centerPotStr = line3.split("=")[1];
		centerPot = Integer.parseInt(centerPotStr);
		String line4 = scan.nextLine();
		String currentPlayerStr = line4.split("=")[1];
		currentPlayerTurn = Integer.parseInt(currentPlayerStr);
		String line5 = scan.nextLine();
		String curPhase = line5.split("=")[1];
		currentPhase = Phase.valueOf(curPhase);
		String line6 = scan.nextLine();
		String qualifications = line6.split("=")[1];
		currentHandQualifications = Integer.parseInt(qualifications);
		scan.close();
	}
	public void dealCards(){
		currentPhase = Phase.BLINDS;
		centerPot = 0;
		currentPlayerTurn = 0;
		currentHandQualifications = 0;
		Stack<Card> cards = DeckCreator.createDeck();
		for (Player p: players){
			p.setCard1(cards.pop());
		}
		for (Player p: players){
			p.setCard2(cards.pop());
		}
		tableCards.clear();
		cards.pop();
		for (int i = 0; i < 3; i++)
			tableCards.add(cards.pop());
		cards.pop();
		tableCards.add(cards.pop());
		cards.pop();
		tableCards.add(cards.pop());
	}
	public String toString(){
		String ret = "";
		ret += "tableCards:{";
		for (Card c: tableCards){
			ret += "{"+c.toString()+"}";
		}
		ret += "}\n";
		ret += "players:{";
		for (Player p: players){
			ret += "{"+p.toString()+ "}";
		}
		ret += "}\n";
		ret += "centerpot="+centerPot+"\n";
		ret += "currentplayerTurn="+currentPlayerTurn+"\n";
		ret += "currentphase="+currentPhase+"\n";
		ret += "currentqualifications="+currentHandQualifications+"\n";
		return ret;
	}
	@Override
	public boolean equals(Object s){
		if (s instanceof GameState){
			GameState state = (GameState)s;
			boolean trip = false; //assume they are equal, the boolean "flag" hasn't been tripped
			for (int i = 0; i < tableCards.size(); i++){
				if (!tableCards.get(i).equals(state.tableCards.get(i))){
					trip = true;
				}
			}
			for (int i = 0; i < players.size(); i++){
				if (!players.get(i).equals(state.players.get(i))){
					trip = true;
				}
			}
			if (centerPot != state.centerPot){
				trip = true;
			}
			if (currentPlayerTurn != state.currentPlayerTurn){
				trip = true;
			}
			if (currentPhase != state.currentPhase){
				trip = true;
			}
			return !trip;
		}
		else{
			return false;
		}
	}
	public String getPlayerList() {
		String ret = "";
		for (int i = 0; i < players.size(); i++){
			ret += players.get(i).getName()+"\n";
		}
		return ret;
	}
	public int currentTurn() {
		return currentPlayerTurn;
	}
	public void takeTurn(int playerNum, TurnAction raise, int i) {
		System.out.print("Turn made");
		Player p = players.get(playerNum);
		if (raise == TurnAction.FOLD){
			p.foldHand();
		}
		else if (raise == TurnAction.CHECKORCALL){
			centerPot += p.withDrawCash(currentHandQualifications - p.getStake());
		}
		else if (raise == TurnAction.RAISE){
			centerPot += p.withDrawCash(i + currentHandQualifications - p.getStake() );
			currentHandQualifications += i;
		}
		p.setPlayed(true);
		currentPlayerTurn = nextAvailablePlayer();
	}
	public int nextAvailablePlayer(){

		if (poolSatisfied()){
			return 0;
		}
		int temp = currentPlayerTurn + 1;
		if (temp == players.size()){
			temp = 0;
		}
		Player p = players.get(temp);
		while (p.hasFolded()){
			temp += 1;
			if (temp == players.size()){
				temp = 0;

			}
			p = players.get(temp);
		}
		return temp;
	}
	private boolean poolSatisfied() {
		int inCount = 0;
		for (Player p: players){//If only one player remains non-folded, the entire hand is satisfied
			if (!p.hasFolded()){
				inCount++;
			}
		}
		if (inCount == 1&&currentPhase != Phase.WINNER){
			currentPhase = Phase.WINNER;
			resetPlayIndicators();
			return true;
		}
		for (Player p: players){//If every player has gone
			if (!p.hasHadchanceToPlay()&&!p.hasFolded()){
				return false;
			}
		}
		for (Player p: players){//If every non-folded player has paid up
			if (p.getStake() != currentHandQualifications && !p.hasFolded()){
				return false;
			}
		}

		if (currentPhase == Phase.BLINDS){
			currentPhase = Phase.FLOP;
		}
		else if (currentPhase == Phase.FLOP){
			currentPhase = Phase.TURN;
		}
		else if (currentPhase == Phase.TURN){
			currentPhase = Phase.RIVER;
		}
		else if (currentPhase == Phase.RIVER){
			currentPhase = Phase.WINNER;
		}
		resetPlayIndicators();
		return true;
	}
	public void resetPlayIndicators(){
		for (Player p: players){
			p.setPlayed(false);
		}
	}
	public void resetHand(){
		for (Player p: players){
			p.newHand();			
		}
	}
	public Phase getPhase() {
		return currentPhase;
	}
	public void nextHand() {
		resetHand();
		dealCards();
		currentPhase = Phase.BLINDS;
		resetPlayIndicators();

	}
	public void payCashToWinner() {
		Player winner = players.get(playerWinnerIndex());
		winner.depositWinnings(centerPot);
		
	}
	private int playerWinnerIndex() {
		ArrayList<Player> thoseWhoRemain = new ArrayList<Player>();
		for (Player p: players){
			if (!p.hasFolded()){
				thoseWhoRemain.add(p);
			}
		}
		int winningIndex = 0;
		HandValue value = handValue(thoseWhoRemain.get(0));
		for (int i = 1; i < players.size(); i++){
			HandValue temp = handValue(players.get(i));
			if (value.betterHand(temp)){
				value = temp;
				winningIndex = i;
			}
		}
		return players.indexOf(thoseWhoRemain.get(winningIndex));
	}
	public HandValue handValue(Player player) {
		ArrayList<Card> cards = new ArrayList<Card>(tableCards);
		cards.add(player.getCard1());
		cards.add(player.getCard2());
		if (HandEvaluator.checkForRoyalFlush(cards) != null){
			return new HandValue(Type.ROYALFLUSH,HandEvaluator.checkForRoyalFlush(cards));
		}
		else if (HandEvaluator.checkForStraightFlush(cards) != null){
			return new HandValue(Type.STRAIGHTFLUSH,HandEvaluator.checkForStraightFlush(cards));
		}
		else if (HandEvaluator.checkForFourOfAKind(cards) != null){
			return new HandValue(Type.FOUROFAKIND,HandEvaluator.checkForFourOfAKind(cards));
		}
		else if (HandEvaluator.checkForFullHouse(cards) != null){
			return new HandValue(Type.FULLHOUSE,HandEvaluator.checkForFullHouse(cards));
		}
		else if (HandEvaluator.checkForFlush(cards) != null){
			return new HandValue(Type.FLUSH,HandEvaluator.checkForFlush(cards));
		}
		else if (HandEvaluator.checkForStraight(cards) != null){
			return new HandValue(Type.STRAIGHT,HandEvaluator.checkForStraight(cards));
		}
		else if (HandEvaluator.checkForThreeOfAKind(cards) != null){
			return new HandValue(Type.THREEOFAKIND,HandEvaluator.checkForThreeOfAKind(cards));
		}
		else if (HandEvaluator.checkForTwoPair(cards) != null){
			return new HandValue(Type.THREEOFAKIND,HandEvaluator.checkForTwoPair(cards));
		}
		else if (HandEvaluator.checkForPair(cards) != null){
			return new HandValue(Type.ONEPAIR,HandEvaluator.checkForPair(cards));
		}
		else{
			return new HandValue(Type.HIGHCARD,HandEvaluator.highestCard(cards));
		}
	}

}
